#ifndef EVOLUTIONELEMENT_H_INCLUDED
#define EVOLUTIONELEMENT_H_INCLUDED

#include "../formula/formula.h"
#include "../formula/formulafactory.h"
#include "../input/input.h"
#include "../util/sort/bubblesort.h"
#include <iostream>

class EvolutionElement {
    private:
        Formula** formulas;
        //  The formulas of this EvolutionElement are stored here.
        unsigned int amountOfFormulas;
        // here we keep the amount of formulas this element has.
        // in other words, the arraysize of formulas
        unsigned int initialFormulaLength;
        // the length the formulas get at creation
        InputIterator& inputIterator;
        // pointer on the Input
        inline void evaluate() {
            for (unsigned int i=amountOfFormulas; i--;) {
                inputIterator.reset();
                formulas[i]->evaluate(inputIterator);
                if(!formulas[i]->wasNeverNan) {
                    createNewFormulaAt(i);
                    inputIterator.reset();
                    formulas[i]->evaluate(inputIterator);
                }
            }
        };
        // evaluates all the formulas
        inline void createNewFormulaAt(unsigned int& i) {
            assert(amountOfFormulas>1);
            delete formulas[i];
            if(rand()%4==1 || i==0)
                formulas[i]=FormulaFactory::createNewFormula(initialFormulaLength,false);
            else
                formulas[i]=formulas[0]->copy();
        }
        // delete and create new formula at formulas position i
        inline void sort() {
            BubbleSort<Formula>::sort(formulas, amountOfFormulas);
        };
        // sort all formulas with bubble sort depending on fitness
        inline void mutate() {
            if(amountOfFormulas > 3) {
                for(unsigned int u=amountOfFormulas/9; u--;) {
                    // overwrite a speciefied part of all contained formulas
                    unsigned int chance = (rand()%(amountOfFormulas-1))+1;
                    delete formulas[chance];
                    formulas[chance]=formulas[0]->copy();
                }

                delete formulas[amountOfFormulas-1];
                formulas[amountOfFormulas-1]=FormulaFactory::createNewFormula(initialFormulaLength, false);
            }

            for (unsigned int i=amountOfFormulas; --i;)
                formulas[i]->mutate();
        };
    public:
        EvolutionElement(unsigned int& amountOfFormulasToCreate, unsigned int& sizeOfNewCreatedFormulas, Input& myInput);
        // full constructor
        ~EvolutionElement();
        // simple destructor
        const double& getBestFitnessScore();
        // returns the fitness of the best formula
        void run(const unsigned int& amountOfRuns);
        // runs this EvolutionElement amountOfRuns times.
        friend bool operator<(EvolutionElement& e1, EvolutionElement& e2);
        // overloading operator <
        friend bool operator>(EvolutionElement& e1, EvolutionElement& e2);
        // overloading operator >
        Formula** getFormulas() {   return formulas;    };
};

#endif // EVOLUTIONELEMENT_H_INCLUDED
